package postgre

import (
	"errors"
	"fmt"
	"myutil/strs"

	"reflect"
	"testing"
	"unsafe"

	"gopkg.in/pg.v4"
	"gopkg.in/pg.v4/orm"
	"gopkg.in/pg.v4/types"
)

// CRUDer interface，使用统一的数据库表操作接口 fk:foreign key，作为传入或传出的参数，只支持单条操作
type CRUDer interface {
	Create(fk interface{}, con *Constraint) (int, error)
	Read(con *Constraint) (fk interface{}, err error)
	Update(con *Constraint) (int, error)
	Delete(con *Constraint) (int, error)
	DeleteByPk(con *Constraint) (int, error)
}

// CRUDser interface，使用统一的数据库表操作接口 fk:foreign key，作为传入或传出的参数，支持单条或多条操作
type CRUDser interface {
	Reads(con *Constraint) (fk interface{}, err error)
}

// Converter 程序结构体转换为数据库表结构体；或者把数据库表结构体转换为程序结构体
type Converter interface {
	Convert2Sql(fk interface{}, model interface{}) error
	Convert2Struct(model interface{}) (fk interface{}, err error)
}

// WhereCond 设置条件属性，操作条件根据需求扩展
type WhereCond struct {
	Key string // 条件字段名称，PK代表主键，删除、更新对外服务必须提供主键，数据处理程序将PK转换为对应的primarykey的字段名称
	//	Op    Operator // 条件运算符，暂不实现，使用最简单的字符串模式
	Op    string      // 条件运算符
	Value interface{} // 条件值字段
}

// Operator 运算符
type Operator byte

// EQL 支持数值类型的大小比较，所有类型的相等不等比较，还支持包含，非包含操作
const (
	EQL  Operator = iota // 相等
	NEQ                  // 不相等
	GT                   // 大于
	GEQ                  // 大于等于
	LT                   // 小于
	LEQ                  // 小于等于
	IN                   // 包含
	NIN                  // 不包含
	LIKE                 // 数据库like
)

// CrudType 对数据的操作类型
type CrudType int16

// UnknowCrud 默认类型未知，也可以用于判断是否设置值
const (
	UnknowCrud CrudType = iota
	CREATE
	SELECT
	UPDATE
	DELETE
)

// Constraint 请求入口设置约束条件，减少处理过程中数据传输量，提升处理效率
type Constraint struct {
	Type       CrudType               // 操作数据类型
	Fields     []string               // 输出限定字段，查询时候使用，返回内容只限定fields内字段，传入时要转化为数据库对应字段
	Columns    map[string]interface{} // 数据库update更新的字段
	Where      []WhereCond            // 输入约束条件，如果对应到关系数据库就是where语句
	Sort       int                    // "1"代表升序；"-1"代表降序；"0"代表不排序
	SortField  string                 // 排序的字段
	Unique     string                 // 合并指定字段重复的条目,为空表示不设置
	Limit      int                    // 分页或输出条目概念，对应数据库语句为limit，对应API接口字段为limit
	Offset     int                    // 偏移位置，结合limit实现分页功能
	OnConflict string                 // 对数据操作产生冲突的处理逻辑，比如对已经存在的主键数据插入冲突问题，另外问题是当插入一个数据时已经存在改为更新数据时
	Fk         interface{}            // 外部依赖的主键
	//	Filter     UsrFilter              // 过滤器
}

// Map2Constraint 根据传入的结构体数据对数据请求条件进行封装，过滤掉默认空值的字段
func (con *Constraint) Map2Constraint(convert map[string]interface{}, where []string) error {

	switch con.Type {
	case CREATE:
		con.OnConflict = "DO NOTHING"
	case SELECT:
		if con.Where == nil {
			con.Where = make([]WhereCond, 0, 1)
		}
		for k, v := range convert {
			wCon := WhereCond{}
			wCon.Key = k
			wCon.Op = "="
			wCon.Value = v
			con.Where = append(con.Where, wCon)
		}
	case UPDATE:
		if con.Where == nil {
			con.Where = make([]WhereCond, 0, 1)
		}
		for _, k := range where {
			if v, ok := convert[k]; ok {
				wCon := WhereCond{}
				wCon.Key = k
				wCon.Op = "="
				wCon.Value = v
				con.Where = append(con.Where, wCon)
				delete(convert, k)
			} else {
				return errors.New("主键为空")
			}
		}

		con.Columns = convert
	case DELETE:
		if con.Where == nil {
			con.Where = make([]WhereCond, 0, 1)
		}
		for k, v := range convert {
			wCon := WhereCond{}
			wCon.Key = k
			wCon.Op = "="
			wCon.Value = v
			con.Where = append(con.Where, wCon)
		}
	}

	return nil
}

// appendModel 拼装sql请求
func appendModel(query *orm.Query, con *Constraint) *orm.Query {
	switch con.Type {
	case CREATE:
		if len(con.Columns) > 0 {
			query = query.Returning(con.Fields)
		}
		if con.OnConflict != "" {
			query = query.OnConflict(con.OnConflict)
		}

	case SELECT:
		lc := len(con.Fields)
		if lc == 0 {
			query = query.Column("*")
		} else {
			query = query.Column(con.Fields...)
		}

		lw := len(con.Where)
		if lw > 0 {
			for i := 0; i < lw; i++ {
				wcon := con.Where[i]
				strwhere := fmt.Sprintf("%s %s ?", wcon.Key, wcon.Op)
				query = query.Where(strwhere, wcon.Value)
			}
		}

		if con.Limit > 0 {
			query = query.Limit(con.Limit)
		}

		if con.Offset > 0 {
			query = query.Offset(con.Offset)
		}

		if con.Sort > 0 {
			query = query.Order(con.SortField, "ASC")
		}
		if con.Sort < 0 {
			query = query.Order(con.SortField, "DESC")
		}

		if con.Unique != "" {
			query = query.Group(con.Unique, "")
		}

	case UPDATE:
		for k, v := range con.Columns {
			strcol := fmt.Sprintf("%s = ?", k)
			query = query.Set(strcol, v)
			//				strcol := fmt.Sprintf("%s = ?", k, k)
			//				query = query.Set(strcol)

		}

		lw := len(con.Where)
		if lw > 0 {
			for i := 0; i < lw; i++ {
				wcon := con.Where[i]
				//				strwhere := fmt.Sprintf("\"%s %s ?%s\"", wcon.Key, wcon.Op, wcon.Key)
				//				query = query.Where(strwhere)
				strwhere := fmt.Sprintf("%s %s ?", wcon.Key, wcon.Op)
				query = query.Where(strwhere, wcon.Value)
			}
		}

	case DELETE:
		lw := len(con.Where)
		if lw > 0 {
			for i := 0; i < lw; i++ {
				wcon := con.Where[i]
				//				strwhere := fmt.Sprintf("\"%s %s ?%s\"", wcon.Key, wcon.Op, wcon.Key)
				//				query = query.Where(strwhere)
				strwhere := fmt.Sprintf("%s %s ?", wcon.Key, wcon.Op)
				query = query.Where(strwhere, wcon.Value)
			}
		}
	}

	return query

}

// CreateRow intert to table
func CreateRow(Db *pg.DB, con *Constraint, model interface{}) (res *types.Result, err error) {
	if con == nil {
		err = Db.Create(model)
		if err != nil {
			return nil, err
		}
	}

	query := Db.Model(model)
	query = appendModel(query, con)
	//	PrintfQuery(query)
	res, err = query.Create()
	if err != nil {
		return nil, err
	}

	return res, nil
}

// ReadRow one row from table
func ReadRow(Db *pg.DB, con *Constraint, model interface{}) error {
	if con == nil {
		return Db.Select(model)
	}

	query := Db.Model(model)
	query = appendModel(query, con)
	// PrintfQuery(query)
	return query.Select()
}

// ReadRows multi rows from table
func ReadRows(Db *pg.DB, con *Constraint, models interface{}) error {
	if con == nil {
		return Db.Select(models)
	}

	query := Db.Model(models)
	query = appendModel(query, con)
	//	PrintfQuery(query)
	return query.Select()
}

// UpdateRow update to table
func UpdateRow(Db *pg.DB, con *Constraint, model interface{}) (res *types.Result, err error) {
	if con == nil {
		err = errors.New("no update fields")
	} else {
		query := Db.Model(model)
		query = appendModel(query, con)
		res, err = query.Update()
	}

	if err != nil {
		return nil, err
	}

	return res, nil
}

// DeleteRows delete one or multi rows from table
func DeleteRows(Db *pg.DB, con *Constraint, model interface{}) (res *types.Result, err error) {
	if con == nil {
		return nil, Db.Delete(model)
	}

	query := Db.Model(model)
	query = appendModel(query, con)
	res, err = query.Delete()

	if err != nil {
		return res, err
	}

	return res, nil
}

// SetPrimaryKey replace string:PK to table real primary key
func SetPrimaryKey(con *Constraint, pk string) bool {
	lw := len(con.Where)
	for i := 0; i < lw; i++ {
		wcon := &con.Where[i]
		if wcon.Key == pk {
			return true
		}
		if wcon.Key == "PK" {
			wcon.Key = pk
			return true
		}
	}

	return false
}

// CreateRowTest 封装TestCreate的测试用例封装，减少代码输入量
func CreateRowTest(t *testing.T, fk interface{}, con *Constraint, model CRUDer, modelName string) {
	n, err := model.Create(fk, con)
	if err != nil {
		errPre := fmt.Sprintf("Test Create %s error:", modelName)
		t.Error(errPre, err)
		return
	}
	if n == 0 {
		errPre := fmt.Sprintf("Test Create %s  error Affected 0 rows", modelName)
		t.Error(errPre)
		return
	}
	return
}

// ReadRowTest 封装TestRead的测试用例封装，减少代码输入量
func ReadRowTest(t *testing.T, con *Constraint, model CRUDer, modelName string) (fk interface{}) {
	fk, err := model.Read(con)
	if err != nil {
		errPre := fmt.Sprintf("Test Read %s error:", modelName)
		t.Error(errPre, err)
		return nil
	}

	t.Logf("Test Read %s OK : %v", modelName, model)
	return fk
}

// ReadRowsTest 封装TestRead的测试用例封装，减少代码输入量
func ReadRowsTest(t *testing.T, con *Constraint, models CRUDser, modelName string) (fk interface{}) {
	fk, err := models.Reads(con)
	fmt.Printf("models : %v\n", models)
	if err != nil {
		errPre := fmt.Sprintf("Test Reads %s error:", modelName)
		t.Error(errPre, err)
		return nil
	}

	t.Logf("Test Reads %s OK : %v", modelName, models)
	return fk
}

// UpdateRowTest 封装TestUpdate的测试用例封装，减少代码输入量
func UpdateRowTest(t *testing.T, con *Constraint, model CRUDer, modelName string) {
	n, err := model.Update(con)
	if err != nil {
		errPre := fmt.Sprintf("Test Update %s error:", modelName)
		t.Error(errPre, err)
		return
	}
	if n == 0 {
		errPre := fmt.Sprintf("Test Update %s  error Affected 0 rows", modelName)
		t.Error(errPre)
		return
	}
	t.Logf("Test Update %s OK : %v", modelName, model)
	return
}

// DeleteRowsTest 封装TestDelete的测试用例封装，减少代码输入量
func DeleteRowsTest(t *testing.T, con *Constraint, model CRUDer, modelName string) {
	n, err := model.Delete(con)
	if err != nil {
		errPre := fmt.Sprintf("Test Delete %s error:", modelName)
		t.Error(errPre, err)
		return
	}
	if n == 0 {
		errPre := fmt.Sprintf("Test Delete %s  error Affected 0 rows", modelName)
		t.Error(errPre)
		return
	}
	t.Logf("Test Delete %s OK : %v", modelName, model)
	return
}

// DeleteByPkTest 封装TestDelete的测试用例封装，减少代码输入量
func DeleteByPkTest(t *testing.T, con *Constraint, model CRUDer, modelName string) {
	n, err := model.DeleteByPk(con)
	if err != nil {
		errPre := fmt.Sprintf("Test DeleteByPk %s error:", modelName)
		t.Error(errPre, err)
		return
	}
	if n == 0 {
		errPre := fmt.Sprintf("Test DeleteByPk %s  error Affected 0 rows", modelName)
		t.Error(errPre)
		return
	}
	t.Logf("Test DeleteByPk %s OK : %v", modelName, model)
	return
}

// PrintfQuery 打印orm.Query结构体非导出成员值
func PrintfQuery(query *orm.Query) {
	t := reflect.TypeOf(query)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	for j := 0; j < t.NumField(); j++ {
		f := t.Field(j)
		uf := unsafe.Pointer(query)
		oset := unsafe.Pointer(uintptr(uf) + uintptr(f.Offset))
		//		fmt.Printf("queryname : %s,type : %s,offset : %d, oset : %v\n", f.Name, f.Type.String(), f.Offset, oset)
		switch f.Type.String() {
		case "types.Q":
			fmt.Printf("queryname : %s,type : %s,offset : %d, osetvalue : %v\n", f.Name, f.Type.String(), f.Offset, string(*(*types.Q)(oset)))
		case "map[string]func(*orm.Query) *orm.Query":
			fmt.Printf("queryname : %s,type : %s,offset : %d, osetvalue : %v\n", f.Name, f.Type.String(), f.Offset, *(*map[string]func(*orm.Query))(oset))
		case "string":
			fmt.Printf("queryname : %s,type : %s,offset : %d, osetvalue : %s\n", f.Name, f.Type.String(), f.Offset, *(*string)(oset))
		case "[]string":
			fmt.Printf("queryname : %s,type : %s,offset : %d, osetvalue : %s\n", f.Name, f.Type.String(), f.Offset, *(*[]string)(oset))
		case "[]uint8":
			fmt.Printf("queryname : %s,type : %s,offset : %d, osetvalue : %v\n", f.Name, f.Type.String(), f.Offset, string(*(*[]uint8)(oset)))
		case "int":
			fmt.Printf("queryname : %s,type : %s,offset : %d, osetvalue : %d\n", f.Name, f.Type.String(), f.Offset, *(*int)(oset))

		}

	}
	/*
		v := reflect.ValueOf(query).Elem()
		fmt.Printf("queryvalue :%v\n", v)
		for i := 0; i < v.NumField(); i++ {
			field := v.Field(i)
			fmt.Printf("queryabcccccc : %d. %s %s = %v \n", i, v.Type().Field(i).Name, field.Type(), field.Interface())

				if va, ok := field.Interface().([]byte); ok {
					fmt.Printf("fieldname : %s, fieldvalue : %s\n", v.Type().Field(i).Name, string(va))
				}

		}
	*/
}

// Struct2SqlCon 把结构体内容变成数据库条件，select和delete转化为where条件，update转化set字段，目前只支持"="条件
// 警告 当条件为类型的0值时，转化会有问题
func Struct2SqlCon(model interface{}) map[string]interface{} {
	cond := make(map[string]interface{})
	object := reflect.ValueOf(model)

	if object.Kind() == reflect.Ptr {
		object = object.Elem()
	}

	typeOfType := object.Type()

	for i := 0; i < object.NumField(); i++ {
		field := object.Field(i)
		//		fmt.Printf("field name : %s,offset %d, size  : %d\n", typeOfType.Field(i).Name, typeOfType.Field(i).Offset, unsafe.Sizeof(field.Interface()))
		switch field.Kind() {
		case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
			if field.Len() > 0 {
				name := strs.Underscore(typeOfType.Field(i).Name)
				cond[name] = field.Interface()
			}
		case reflect.Bool:
			if field.Bool() {
				name := strs.Underscore(typeOfType.Field(i).Name)
				cond[name] = field.Interface()
			}
		case reflect.Int, reflect.Int16, reflect.Int64, reflect.Int8:
			if field.Int() != 0 {
				name := strs.Underscore(typeOfType.Field(i).Name)
				cond[name] = field.Interface()
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.UnsafePointer:
			if field.Uint() != 0 {
				name := strs.Underscore(typeOfType.Field(i).Name)
				cond[name] = field.Interface()
			}
		case reflect.Float32, reflect.Float64:
			if field.Float() != 0 {
				name := strs.Underscore(typeOfType.Field(i).Name)
				cond[name] = field.Interface()
			}
		case reflect.Interface, reflect.Ptr, reflect.Chan, reflect.Func:
			if !field.IsNil() {
				name := strs.Underscore(typeOfType.Field(i).Name)
				cond[name] = field.Interface()
			}
		case reflect.Struct:
			if v := field.MethodByName("IsZero"); v.IsValid() {
				if v.Call([]reflect.Value{})[0].Bool() == true {
					break
				}
			}

			if IsNilStruct(object, i) {
				break
			}
			name := strs.Underscore(typeOfType.Field(i).Name)
			cond[name] = field.Interface()
			/*
				if name == "table_name" {
					break
				}
				if unsafe.Sizeof(field) > 0 {
					//			name := orm.Underscore(typeOfType.Field(i).Name)
					cond[name] = field.Interface()
				}
			*/
		}

		//		fmt.Printf("%d. %s %s = %v \n", i, typeOfType.Field(i).Name, field.Type(), field.Interface())
	}

	return cond
}

// IsNilStruct 判断结构体是否没有初始化
func IsNilStruct(object reflect.Value, i int) bool {
	typeOfType := object.Type()
	len := object.NumField()

	field := object.Field(i)
	if i+1 < len {
		return typeOfType.Field(i).Offset == typeOfType.Field(i+1).Offset
	} else if i+1 == len {
		return typeOfType.Size() != typeOfType.Field(i).Offset+unsafe.Sizeof(field.Interface())
	}

	return false
}
